using System;
using System.Collections.Generic;

namespace DreamPark
{
	public class Parking
	{
		private List<Place> listePlaces;
		private Acces[] listeAcces;
		private List<Trace> listeTraces;
		private List<Ticket> listeTickets;
		private int placesMax;
		private int placesLibres;
		
		/* PROPRIETES */
		
		public List<Place> Places {
			get { return listePlaces; }
		}
		
		public Acces[] Acces {
			get { return listeAcces; }
			set { listeAcces = value; }
		}
		
		public List<Trace> Traces {
			get { return listeTraces; }
		}
		
		public List<Ticket> Tickets {
			get { return listeTickets; }
		}
		
		public int PlacesMax {
			get { return placesMax; }
			set { placesMax = value > 0 ? value : 0; }
		}
		
		public int PlacesLibres {
			get { return placesLibres; }
			set { placesLibres = value > 0 ? value : 0; }
		}
		
		/* CONSTRUCTEUR */
		
		public Parking (int placesMax) {
			this.placesMax = placesMax;

			this.listeTraces = new List<Trace>();
			this.listePlaces = new List<Place>();
			this.listeAcces = new Acces[2];
			this.listeTickets = new List<Ticket>();
			
			Teleporteur t1 = new Teleporteur(1);
			Teleporteur t2 = new Teleporteur(2);
			Teleporteur t3 = new Teleporteur(1);
			Teleporteur t4 = new Teleporteur(2);
			Acces acces1 = new Acces(1, t1, t2);
			Acces acces2 = new Acces(2, t3, t4);
			this.listeAcces[0] = acces1;
			this.listeAcces[1] = acces2;
			
			this.placesLibres = listePlaces.Count;
		}
		
		/* METHODES */
		
		public void AjouterPlace(Place place) {
			if (listePlaces.Count < placesMax) {
				listePlaces.Add(place);
			} else {
				throw new ParkingPleinException();
			}
			this.placesLibres += 1;
		}
		
		public void AjouterTrace(Trace trace) {
			listeTraces.Add(trace);
		}
	
		// Méthode permettant de gérer le cas d'utilisation " Garer Vehicule "
		public Ticket GarerVehicule(int numeroAcces, TypeVehicule type) {
			
			if (placesLibres == 0) {
				throw new ParkingCompletException();
			}
			
			int i = 0;
			Acces courantAcces = listeAcces[i];
			// On récupère l'accès par lequel le véhicule entre, puis les caractéristiques de celui-ci
			while (courantAcces.NumeroAcces != numeroAcces) { i++; courantAcces = listeAcces[i]; }

			Vehicule vehiculeAGarer = courantAcces.Camera.CapturerVehicule(type);
			
			// On récupère une place libre afin de lui assigner le véhicule entrant
			Place placeOccupee = null;
			foreach (Place place in listePlaces) {
				if (!place.Occupee && place.Hauteur > vehiculeAGarer.Hauteur 
				    && place.Longueur > vehiculeAGarer.Longueur) {
					place.Occupee = true;
					place.VehiculeOccupant = vehiculeAGarer;
					placeOccupee = place;
					break;
				}
			}
			
			if (placeOccupee == null) {
				throw new VehiculeTropImportantException();
			}
			
			// On génére le ticket à destination du client
			Ticket ticketGenere = courantAcces.Borne.GenererTicket(placeOccupee, vehiculeAGarer.Immatriculation);
			listeTickets.Add(ticketGenere);
			Console.WriteLine(ticketGenere.NumeroTicket);
			
			// On génére et on ajoute la trace de l'entrée du véhicule dans le parking
			Trace trace = new Trace(vehiculeAGarer, DateTime.Now.ToString("g"), courantAcces);
			listeTraces.Add(trace);
			
			// On incrémente la valeur des panneaux d'affichage de chaque accès
			MiseAJourPanneauxAffichage(true);
			
			this.placesLibres -= 1;
			
			return ticketGenere;
		}
		
		// Méthode permettant de gérer le cas d'utilisation " Reprendre Vehicule "
		public bool ReprendreVehicule(string numeroTicket) {
			Ticket ticket = null;
			
			foreach (Ticket ticketCourant in listeTickets) {
				if (ticketCourant.NumeroTicket.Equals(numeroTicket)) {
					ticket = ticketCourant;
					break;
				}
			}
			// Si le ticket n'existe pas, on retourne faux
			if (ticket == null) { return false; }
			
			// On recherche le vehicule dans la liste des places
			Vehicule vehiculeASortir = null;
			foreach (Place place in listePlaces) {
				if (place.NumeroPlace == ticket.Place.NumeroPlace) {
					vehiculeASortir = place.VehiculeOccupant;
					place.VehiculeOccupant = null;
					place.Occupee = false;
					break;
				}
			}
			
			MiseAJourPanneauxAffichage(false);
			
			// Ajouter la date de sortie à la trace correspondante
			foreach (var trace in listeTraces) {
				if (trace.Vehicule == vehiculeASortir) {
					trace.DateSortie = DateTime.Now.ToString("g");
					
					Trace.sauvegarde("Traces.xml", trace);
					break;
				}
			}
			
			this.placesLibres += 1;
			return true;
		}
		
		// On met a jour les panneaux d'affichage de chacun des acces
		private void MiseAJourPanneauxAffichage(bool incremente) {
			for (int i = 0; i < listeAcces.Length; i++) {
				if (incremente) {
					listeAcces[i].Panneau.CompteurPlacesLibres += 1;
				} else {
					listeAcces[i].Panneau.CompteurPlacesLibres -= 1;
				}
			}
		}
		
		
		// Recupere le temps ecoule pour un client
		public TimeSpan GetTempsEcoule(string numeroTicket) {
			Ticket ticket = null;
			
			foreach (Ticket ticketCourant in listeTickets) {
				if (ticketCourant.NumeroTicket.Equals(numeroTicket)) {
					ticket = ticketCourant;
					break;
				}
			}
			
			TimeSpan tempsEcoule;
			foreach (var trace in listeTraces) {
				if (trace.Vehicule.Immatriculation == ticket.ImmatriculationVehicule) {
					tempsEcoule = DateTime.Parse(trace.DateSortie).Subtract(DateTime.Parse(trace.DateEntree));
					break;
				}
			}
			return tempsEcoule;
		}
	}
}